/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.camelspit.preludetoarmageddon.entity;
import com.camelspit.preludetoarmageddon.places.*;
import com.camelspit.preludetoarmageddon.util.*;
import java.net.*;
import java.io.*;
import java.util.Arrays;
import java.util.Formatter;
import java.util.FormatterClosedException;
/**
 *
 * @author DMK
 */
public class Player extends BaseEntity implements Serializable{
    //Max player Items
    static final int PLAYER_ITEMS = 16;
    //Base File Location
    String basePlayerLoc = "/com/camelspit/preludetoarmageddon/data/players/";
    //Player information
    String name;
    String password;
    PlayerRanks rank;

    //Player attributes
    int statPoints;
    int experience;
    int level;
    Room room;
    Money money;
    int hitPoints;
    EntityAttributes attributes; //includes base attributes
    long nextAttackTime;

    //player inventory
    Item[] inventory;
    int items;
    int weapon;
    int armor;

    //Non-savable info
    //connection<telnet> connection - to be implemented later
    boolean loggedIn;
    boolean active;
    boolean newbie;

    public Player() {
        password = "UNDEFINED";
        name = "DEFAULT";
        rank = PlayerRanks.REGULAR;
        //connection = 0;
        loggedIn = false;
        active = false;
        newbie = true;
        experience = 0;
        level = 1;
        room = new Room();
        money = new Money();
        attributes = new EntityAttributes();
        attributes.setBaseStat(Attributes.STAMINA, 1);
        attributes.setBaseStat(Attributes.STRENGTH, 1);
        attributes.setBaseStat(Attributes.DEXTERITY, 1);
        attributes.setBaseStat(Attributes.INTELLIGENCE, 1);
        statPoints = 18;
        items = 0;
        weapon = -1;
        armor = -1;
        recalculateStats();
        hitPoints = getAttr(Attributes.MAX_HITPOINTS);
        inventory = new Item[PLAYER_ITEMS];
        //Arrays.fill(inventory, null);

    }

    public void setPlayerName(String name) {
        this.name = name;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setRank(String rank) {
        for(PlayerRanks rankItr: PlayerRanks.values()) {
            if(rankItr.toString().equalsIgnoreCase(rank))
                this.rank = rankItr;
        }
    }

    public void setRank(int rank) {
        for(PlayerRanks rankItr: PlayerRanks.values()) {
            if(rankItr.getValue() == rank)
                this.rank = rankItr;
        }
    }

    public void setExperience(int exp) {
        this.experience = exp;
    }

    public void setLevel(int lvl) {
        this.level = lvl;
    }

    public void setRoom(int roomID) {
        this.room.setID(roomID);
    }

    public void setHitPoints(int hp) {
        this.hitPoints = hp;
    }

    public void setNextAttackTime(int attackTime) {
        this.nextAttackTime = (long)attackTime;
    }

    public void setLoggedIn(boolean loggedIn) {
        this.loggedIn = loggedIn;
    }

    public void setActive(boolean active) {
        this.active = active;
    }

    public String name() {return name;}
    /**
     * Determines how much experience a player needs  for a specific
     * level. Uses an exponential function to make leveling progressively
     * more difficult.
     *
     * @param level
     * @return
     */
    public static int needForLevel(int level) {
        return (int)(100 * (Math.pow(1.4, level - 1) - 1));
    }

    /**
     * Returns the number of experience points left until the player
     * is able to level.
     * @return
     */
    public int needForNextLevel() {
        return needForLevel(level + 1) - experience;
    }

    public int level() {return level;}

    /**
     * If you have reached the amount of experience needed to go to
     * the next level, you receive two stat points, health equivalent
     * to your current level and recalculate your stats.
     *
     * @return
     */
    public boolean levelUp() {
        if(needForNextLevel() <= 0) {
            statPoints += 2;
            attributes.addToAttribute(Attributes.HEALTH, level);
            level++;
            recalculateStats();
            return true;
        }
        return false;
    }

    /**
     * Recalculate stats goes through all the dynamic attributes and
     * recalculates them based on level and other attributes that
     * may have changed.
     *
     */
    public void recalculateStats() {
        //Stamina determines MAX_HITPOINTS, HP_REGEN
        attributes.setAttribute(Attributes.MAX_HITPOINTS, (int)(10 + (
                level * (getAttr(Attributes.STAMINA) / 1.5))));
        attributes.setAttribute(Attributes.HP_REGEN,
                (getAttr(Attributes.STAMINA) / 5) + level);
        //Dexterity determines MeleeAccuracy and dodging
        attributes.setAttribute(Attributes.MELEE_ACCURACY,
                (getAttr(Attributes.DEXTERITY) * 3));
        attributes.setAttribute(Attributes.DODGE,
                (getAttr(Attributes.DEXTERITY) * 3));
        //Strength determines Physical Fortitude and (possibly) Dmg
        attributes.setAttribute(Attributes.PHYSICAL_FORTITUDE,
                (getAttr(Attributes.STRENGTH) / 5));
        //attributes.setAttribute(Attributes.STRIKEDAMAGE,
                //(attributes.getBaseStat(BaseAttributes.STRENGTH) / 5));
        if(hitPoints > attributes.getAttribute(Attributes.MAX_HITPOINTS)) {
            hitPoints = attributes.getAttribute(Attributes.MAX_HITPOINTS);
        }

        if(weapon() != null) {
            addDynamicBonuses(weapon());
        }
        if(armor() != null) {
            addDynamicBonuses(armor());
        }
    }

    public int hitPoints() {return hitPoints;}

    public void addHitPoints(int hitPoints) {
        this.hitPoints += hitPoints;
        if(this.hitPoints < 0)
            this.hitPoints = 0;
        if(this.hitPoints > getAttr(Attributes.MAX_HITPOINTS));
           this.hitPoints = getAttr(Attributes.MAX_HITPOINTS);
    }

    /**
     * Adds dynamic and base attributes
     */
    public int getAttr(Attributes attr) {
        int val = attributes.getAttribute(attr) +
                attributes.getBaseStat(attr);
        if(attr == Attributes.STRENGTH ||
           attr == Attributes.DEXTERITY ||
           attr == Attributes.HEALTH ||
           attr == Attributes.INTELLIGENCE)
        {
            if(val < 1) return 1;
        }
        return val;
    }

    public int getBaseAttr(Attributes attr) {
        return attributes.getBaseStat(attr);
    }
    public void setBaseAttr(Attributes attr, int val) {
        attributes.setBaseStat(attr, val);
        recalculateStats();
    }

    public void addToBaseAttr(Attributes attr, int val) {
        attributes.addToBaseAttr(attr, val);
        recalculateStats();
    }

    public int statPoints() {
        return statPoints;
    }

    public void setStatPoints(int points) {
        statPoints = points;
    }

    public void addToStatPoints(int points) {
        statPoints += points;
    }

    public Room currentRoom() {
        return room;
    }

    public Money money() {
        return money;
    }

    public long nextAttackTime() {
        return nextAttackTime;
    }

    public Item getItem(int index) {return inventory[index];}

    public int items() {return items;}

    public int maxItems() {return PLAYER_ITEMS;}

    public Item weapon() {
        if(weapon == -1) {
            return null;
        }
        else
            return inventory[weapon];
    }

    public Item armor() {
        if(armor == -1) {
            return null;
        }
        else
            return inventory[armor];
    }

    /**
     * Adds temporary bonuses
     * @param item
     */
    public void addDynamicBonuses(Item item) {
        if(item == null) {
            return;
        }
        for(Attributes attr: Attributes.values()) {
            attributes.addToAttribute(attr, item.getAttribute(attr));
        }
    }

    /**
     * Adds permanent bonuses
     * @param item
     */
    public void addBonuses(Item item) {
        if(item == null) {
            return;
        }
        for(Attributes attr: Attributes.values()) {
            attributes.setBaseStat(attr, item.getAttribute(attr));
        }
        recalculateStats();
    }

    public void removeBonuses(Item item) {
        if(item == null)
            return;
        for(Attributes attr: Attributes.values()) {
            attributes.addToAttribute(attr, -(item.getAttribute(attr)));
        }
    }
    public boolean pickUpItem(Item item) {
        if(items < maxItems()) {
            int itr = 0;
            while(inventory[itr] != null)
                itr++;
            inventory[itr] = item;
            items++;
            return true;
        }
        return false;
    }

    public boolean dropItem(int index) {
        if(inventory[index] != null) {
            if(weapon == index)
                removeWeapon();
            if(armor == index)
                removeArmor();
            inventory[index] = null;
            items--;
            return true;
        }
        return false;
    }

    public void removeWeapon() {
        weapon = -1;
        recalculateStats();
    }

    public void removeArmor() {
        armor = -1;
        recalculateStats();
    }

    /**
     * To arm with a weapon you first have to remove the current
     * weapon and then put the new weapon as the equiped weapon
     */
    public void armWeapon(int index) {
        removeWeapon();
        weapon = index;
        recalculateStats();
    }

    public void armArmor(int index) {
        removeArmor();
        armor = index;
        recalculateStats();
    }

    public int getItemIndex(String itemName) {
        for(int i = 0; i < inventory.length; i++) {
            if(inventory[i].getName().equalsIgnoreCase(itemName))
                return i;
        }
        return -1;
    }

    public String password() {return password;}

    public PlayerRanks rank() {return rank;}

    //add connection<telnet> conn() {return connection;}

    public boolean loggedIn() {return loggedIn;}

    public boolean newbie() {return newbie;}

    public void sendString(String string) {
        //TODO When able to do connections make this check a connection
        //and send the string to the person
    }

    public void printStatBar() {
        //TODO add statBar
    }
    /**
     * Saves the file in the following format:
     * NAME
     * PASSWORD
     * RANK
     * STATPOINTS
     * EXPERIENCE
     * LEVEL
     * ROOM
     * MONEY
     * HITPOINTS
     * BASEATTRIBUTES 1-4
     * NEXTATTACKTIME
     * INVENTORY 1-16
     * @return
     */
    public boolean save() throws URISyntaxException, IOException {
        String format = "%s%n%s%n%s%n%d%n%d%n%d%n%d%n%d%n%d%n" +
                        "%d%n%d%n%d%n%d%n%d%n%d %d %d %d " +
                        "%d %d %d %d %d %d %d %d %d %d %d %d";
        Formatter output;
        File playerFile;
            playerFile = new File(this.name() + ".plr");
            if(!playerFile.exists())
                playerFile.createNewFile();
        
        output = new Formatter(playerFile);
        output.format(format,
                this.name,
                this.password,
                this.rank.getValue(),
                this.statPoints,
                this.experience,
                this.level,
                this.room.getID(),
                this.money.getMoney(),
                this.hitPoints,
                this.nextAttackTime,
                this.attributes.getBaseStat(Attributes.STAMINA),
                this.attributes.getBaseStat(Attributes.STRENGTH),
                this.attributes.getBaseStat(Attributes.DEXTERITY),
                this.attributes.getBaseStat(Attributes.INTELLIGENCE),
                getInventoryId(1),
                getInventoryId(2),
                getInventoryId(3),
                getInventoryId(4),
                getInventoryId(5),
                getInventoryId(6),
                getInventoryId(7),
                getInventoryId(8),
                getInventoryId(9),
                getInventoryId(10),
                getInventoryId(11),
                getInventoryId(12),
                getInventoryId(13),
                getInventoryId(14),
                getInventoryId(15),
                getInventoryId(16));
    output.close();
    return true;
    }

    public void load() {
        File playerFile = new File((this.name) + ".plr");
    }
    /**
     * Returns the inventory item in the given slot starting at
     * slot 1 and ending at slot 16.
     * @param slot
     * @return
     */
    public int getInventoryId(int slot) {
        if(inventory[slot-1] == null)
            return 0;
        else
            return inventory[slot-1].getID();
    }


}
